﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DisplayBoardInfo.Network;
using DisplayBoardInfo;

namespace DisplayBoardInfo.Server.Session
{
    /// <summary>
    /// Controls current user session.
    /// </summary>
    class SessionManager
    {
        #region Events

        public delegate void UserLoggedHandler(object sender, SessionEventArgs e);
        public event UserLoggedHandler UserLogged;
        public event UserLoggedHandler UserLogout;

        #endregion

        private Network.Server networkServer;
        private Dictionary<int, LoggedUser> loggedUsers;

        public SessionManager()
        {

        }

        /// <summary>
        /// Starts session
        /// </summary>
        public void Start()
        {
            //
            //Check for database
            if (!Model.Database.DatabaseExists())
            {
                Model.Database.CreateDatabase();
                Logger.Logger.Message(Logger.LogMessageType.Info, "Utworzono bazę danych: " + Model.Database.Name + ".");
            }

            //
            //Initialize network server for comunication
            loggedUsers = new Dictionary<int, LoggedUser>();
            networkServer = new Network.Server();
            networkServer.MessageRecived += new Network.Server.MessageRecivedHandler(networkServer_MessageRecived);
            networkServer.Start();
            networkServer.ClientDisconnected += new Network.Server.MessageRecivedHandler(networkServer_ClientDisconnected);
            Logger.Logger.Message(Logger.LogMessageType.Info, "Serwer uruchomiony na porcie 9000");
            //
        }

        void networkServer_ClientDisconnected(object sender, MessageEventArgs args)
        {
            try
            {
                int userID = args.Reciver;
                UserLogout(this, new SessionEventArgs(loggedUsers[userID].User, loggedUsers[userID].DataManager));
                loggedUsers.Remove(userID);
            }
            catch (KeyNotFoundException)
            { 
            //jeszcze się nie zalogował, bywa
            }
        }

        /// <summary>
        /// Stop session
        /// </summary>
        public void Stop()
        {
            networkServer.Stop();
            Logger.Logger.Message(Logger.LogMessageType.Info, "Serwer zatrzymany");
        }

        private void networkServer_MessageRecived(object sender, MessageEventArgs e)
        {
            if (e.Message.Type == Network.Messages.NetworkMessageTypes.Hello)
            {
                Network.Messages.NetworkMessage messageToBack = HandleHelloMessage(e.Message);
                if (messageToBack.Type == Network.Messages.NetworkMessageTypes.Hello)
                {
                    Network.Messages.Content.User nuser = (Network.Messages.Content.User)messageToBack.Content;
                    Entities.User user = new Entities.User(nuser.Username);
                    loggedUsers.Add(e.Reciver, new LoggedUser(new Entities.User(user.Username), e.Reciver));
                    UserLogged(this, new SessionEventArgs(user, loggedUsers[e.Reciver].DataManager));
                }
                networkServer.Send(e.Reciver, messageToBack);
            }
            else
            {
                if (loggedUsers.ContainsKey(e.Reciver)) //if client who send message is logged
                {
                    MessageHandlers.MessageHandler mh = null;
                    if (e.Message.Type == Network.Messages.NetworkMessageTypes.Text)
                    {
                        Logger.Logger.Message(Logger.LogMessageType.Info, e.Message.Content.ToString());
                    }
                    else if (e.Message.Type == Network.Messages.NetworkMessageTypes.GetAppointments)
                    {
                        mh = new MessageHandlers.AppointmentsHandler((Network.Messages.Content.AppointmentArgs)e.Message.Content, loggedUsers[e.Reciver].DataManager);
                        networkServer.Send(e.Reciver, mh.GetResult());
                    }
                    else if (e.Message.Type == Network.Messages.NetworkMessageTypes.AddAppointment)
                    {
                        mh = new MessageHandlers.AddAppointmentsHandler((IEnumerable<Network.Messages.Content.Appointment>)e.Message.Content);
                        Network.Messages.NetworkMessage m = mh.GetResult();
                        networkServer.Send(e.Reciver, m);
                    }
                    else if (e.Message.Type.ToString().Substring(0, 7) == "Session")
                    {
                        mh = new MessageHandlers.SessionHandler(e.Message, loggedUsers[e.Reciver].DataManager);
                        Network.Messages.NetworkMessage m = mh.GetResult();
                        networkServer.Send(e.Reciver, m);
                    }
                    
                    //Handle message
                }
                else networkServer.Send(e.Reciver, new Network.Messages.NetworkMessage(Network.Messages.NetworkMessageTypes.Error, "Użytkownik nie jest zalogowany."));
            }
        }

        private Network.Messages.NetworkMessage HandleHelloMessage(Network.Messages.NetworkMessage sendedMessage)
        {
            Network.Messages.Content.Hello message = (Network.Messages.Content.Hello)sendedMessage.Content;
            if (Entities.Users.IsUserExists(message.Username))
            {
                //TODO: Poprawić
                //if (!loggedUsers.Keys.Contains(message.Username)) 
                if (true)
                {
                    Entities.User user = Entities.Users.GetByUsername(message.Username);
                    if (Helpers.Data.GetSHAHash(message.Password) == user.Password)
                    {
                        Logger.Logger.Message(Logger.LogMessageType.Info, "Zalogowany: " + user.ToString());
                        Network.Messages.Content.User u = new Network.Messages.Content.User();
                        u.Username = user.Username;
                        u.Lastname = user.LastName;
                        //u.Photo = user.Photo;
                        u.Role = user.Role.Name;
                        return new Network.Messages.NetworkMessage(Network.Messages.NetworkMessageTypes.Hello, u);
                    }
                    else return new Network.Messages.NetworkMessage(Network.Messages.NetworkMessageTypes.Error, "Błędne hasło.");
                }
                else return new Network.Messages.NetworkMessage(Network.Messages.NetworkMessageTypes.Error, "Użytkownik już zalogowany.");
            }
            else return new Network.Messages.NetworkMessage(Network.Messages.NetworkMessageTypes.Error, "Użytkownik nie istnieje.");
        }

        /// <summary>
        /// Gets all data managers of all signed doctors.
        /// </summary>
        /// <returns>Collection of DataManager</returns>
        public IEnumerable<DataManager> GetDataManagers()
        {
            List<DataManager> output = new List<DataManager>();
            foreach (LoggedUser item in loggedUsers.Values)
            {
                if (item.DataManager != null) output.Add(item.DataManager);
            }

            return output;
        }
    }
}
